/*
 * Copyright © 2008 University of Houston
 * All rights reserved.
 */

#include <stdio.h>
#include <iostream>
#include <math.h>
#include <string>
#include "analyzer.h"
#include "util.h"
#include "logger.h"
#include "session.h"
#include "filter.h"
#include "algorithm.h"

using namespace std;

/*
 * Start Filter Class
 */
filter* filter::getFilter(int fil)
{
	filter* p = NULL;

	switch (fil)
	{
		case 0:	p = new filter(); 				break;
		case 1: p = new nearestHopFilter(); 	break;
		case 2: p = new userDelayFilter(); 		break;
		case 3: p = new encryptedInFilter(); 	break;
		case 4: p = new encryptedOutFilter(); 	break;
		case 5: p = new commandFilter(); 		break;
		case 6: p = new naiveEchoFilter();		break;
		case 7: p = new streamFilter(); 		break;
	}
	
	if(p == NULL)
		WARNING("Invalid filter type specified!")
	else
		VDEBUG("Filter Created: " << p->getName())
	
	return p;
}

string filter::getFilterName(int filterID)
{
	filter *f = getFilter(filterID);
	string retVal = f->getName();
	delete f;
	
	return retVal;
}

int filter::update(timedHeader *th)
{
	return 0;
}

/*
 * Start NearestHop filter
 */
void nearestHopFilter::initializeVars()
{
	last_th_seq = last_th_ack = 0;
	ready = false;
	sensitivity = 3;
	count = totalTime = 0;
	lastPktTime = -1;
	estimatedRtt = .05; // Initial guess
}

int nearestHopFilter::update(timedHeader *th)
{
	//TODO: Check why this method gives odd values at times??
	/*if (th->isAck() && th->getPayloadSize() == 0)
		return ALG_DO_ANALYSIS;
	return ALG_DO_UPDATE;*/

	tcp_seq th_seq = th->getSequence();
	tcp_seq th_ack = th->getAck();
	long double ts = ttod(th->getTimestamp());

	int status = ALG_DO_UPDATE;

	if (lastPktTime == -1)
	{
		lastPktTime = ts;
		return status;
	}

	// Non-Match
	if ((th_seq != last_th_seq && th_ack != last_th_ack) && ready)
	{
		long double interval = ts - lastPktTime;
		count++;
		totalTime += interval;
		estimatedRtt = totalTime / count;
		status = ALG_DO_ANALYSIS;
	}

	ready = true;

	// Match
	if (th_seq == last_th_seq && th_ack == last_th_ack)
	{
		ready = false;
	}

	last_th_seq = th_seq;
	last_th_ack = th_ack;
	lastPktTime = ts;

	return status;
}

/*
 * Start userDelay filter
 */
void userDelayFilter::initializeVars()
{
	last_th_seq = last_th_ack = 0;
}

int userDelayFilter::update(timedHeader *th)
{
	tcp_seq th_seq = th->getSequence();
	tcp_seq th_ack = th->getAck();

	int status = ALG_DO_UPDATE;

	if (th_seq == last_th_seq && th_ack == last_th_ack)
	{
		status = ALG_DO_ANALYSIS;
	}

	last_th_seq = th_seq;
	last_th_ack = th_ack;
	return status;
}

/*
 * Start encryptedInFilter
 */
int encryptedInFilter::update(timedHeader *th)
{
	if (th->getSourcePort() == 22 && th->isPush())
		return ALG_DO_ANALYSIS;
	return ALG_DO_NOTHING;
}

/*
 * Start encryptedOutFilter
 */
int encryptedOutFilter::update(timedHeader *th)
{
	if (th->getDestPort() == 22 && th->isPush())
		return ALG_DO_ANALYSIS;
	return ALG_DO_NOTHING;
}

/*
 * Start commandFilter
 */
void commandFilter::initializeVars()
{
	sensitivity = 3;
	pktsSinceChar = charCount = 0;
	last_th_seq = last_th_ack = 0;
}

int commandFilter::update(timedHeader *th)
{
	tcp_seq th_seq = th->getSequence();
	tcp_seq th_ack = th->getAck();

	int status = ALG_DO_NOTHING;

	pktsSinceChar++;

	if (th_seq == last_th_seq && th_ack == last_th_ack)
	{
		pktsSinceChar = 0;
		charCount++;
	}

	if (pktsSinceChar > sensitivity && charCount > 0)
	{
		pktsSinceChar = 0;
		charCount = 0;
		status = ALG_DO_ANALYSIS;
	}

	last_th_seq = th_seq;
	last_th_ack = th_ack;
	return status;
}

/*
 * Start naiveEchoFilter
 */
void naiveEchoFilter::initializeVars()
{
	ready = false;
}

int naiveEchoFilter::update(timedHeader *th)
{
	// Outgoing Encrypted Packet
	if (th->getSourcePort() == 22 && th->isPush())
	{
		ready = true;
		return ALG_DO_UPDATE;
	}

	// Incoming Encrypted Packet
	if (th->getDestPort() == 22 && th->isPush() && ready)
	{
		ready = false;
		return ALG_DO_ANALYSIS;
	}
	return ALG_DO_NOTHING;
}

/*
 * Start streamFilter
 */
void streamFilter::initializeVars()
{
	last_th_seq = last_th_ack = -1;
}

int streamFilter::update(timedHeader *th)
{
	tcp_seq th_seq = th->getSequence();
	tcp_seq th_ack = th->getAck();

	int status = ALG_DO_NOTHING;

	if (th_seq == last_th_ack && th_ack == last_th_seq)
	{
		status = ALG_DO_ANALYSIS;
	}

	last_th_seq = th_seq;
	last_th_ack = th_ack;
	return status;
}

